home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 July: Mac OS SDK / Dev.CD Jul 96 SDK / Dev.CD Jul 96 SDK1.toast / Development Kits (Disc 1) / Apple Game Sprockets / Examples / DroneZone / DZDrone.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-16  |  46.6 KB  |  1,702 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    File:        DZDrone.c
  3.  *    Author:        Dan Venolia
  4.  *
  5.  *    Contents:    A drone object represents the position of an player or drone.
  6.  *
  7.  *    Copyright © 1996 Apple Computer, Inc.
  8.  */
  9.  
  10. #include <assert.h>
  11. #include <math.h>
  12. #include <stdlib.h>
  13.  
  14. #include <Components.h>
  15. #include <Dialogs.h>
  16. #include <Events.h>
  17. #include <QuickDraw.h>
  18. #include <Resources.h>
  19. #include <Sound.h>
  20. #include <SoundInput.h>
  21. #include <Types.h>
  22.  
  23. #include <QD3D.h>
  24. #include <QD3DCamera.h>
  25. #include <QD3DDrawContext.h>
  26. #include <QD3DGeometry.h>
  27. #include <QD3DMath.h>
  28. #include <QD3DPick.h>
  29. #include <QD3DSet.h>
  30. #include <QD3DShader.h>
  31. #include <QD3DStyle.h>
  32. #include <QD3DTransform.h>
  33. #include <QD3DView.h>
  34.  
  35. #include "SoundSprocket.h"
  36.  
  37. #include "DZDisplay.h"
  38. #include "DZDrone.h"
  39. #include "DZGame.h"
  40. #include "DZResource.h"
  41. #include "DZSound.h"
  42. #include "DZThumbprint.h"
  43. #include "DZUtils.h"
  44.  
  45. #define IS_SELF_DRONE(inDrone)        (inDrone->thumbprint == kThumbprint_SelfDrone)
  46. #define IS_AUTO_DRONE(inDrone)        (inDrone->thumbprint == kThumbprint_AutoDrone)
  47. #define IS_BULLET_DRONE(inDrone)    (inDrone->thumbprint == kThumbprint_BulletDrone)
  48. #define IS_DRONE(inDrone)            (IS_SELF_DRONE(inDrone) || IS_AUTO_DRONE(inDrone) || IS_BULLET_DRONE(inDrone))
  49.  
  50. #define SELF_DRONE_SPEED            1.0        // In units per second
  51.  
  52. #define AUTO_DRONE_LEAD                5.0        // Drone target leads by this distance (in units)
  53. #define AUTO_DRONE_ACCEL            2.0        // Drone acceleration (units per second per second)
  54. #define AUTO_DRONE_TURN_RATE        1.8        // In radians per second
  55. #define AUTO_DRONE_BURN_TIME        3.0        // In seconds
  56. #define AUTO_DRONE_REST_TIME        1.0        // In seconds
  57. #define AUTO_DRONE_EXPLOSION_TIME    3.0        // In seconds
  58. #define AUTO_DRONE_FADE_TIME        0.25    // Fraction of AUTO_DRONE_EXPLOSION_TIME for fade
  59. #define AUTO_DRONE_MIN_SCALE        0.2        // Initial scale for explosion
  60.  
  61. #define AUTO_DRONE_IDLE_REF_DIST    2.0
  62. #define AUTO_DRONE_BURN_REF_DIST    5.0
  63. #define AUTO_DRONE_EXPL_REF_DIST    30.0
  64.  
  65. #define BULLET_DRONE_OFFSET            0.2        // Offset from self to bullet initial position
  66. #define BULLET_DRONE_SPEED            50.0    // In units per second
  67. #define BULLET_DRONE_LIMIT            50.0    // Maximum length of a bullet
  68.  
  69. #define HUD_SCALE                    0.02    // Scale of HUD
  70. #define HUD_HEIGHT                    0.5        // cos(angle) at which to consider out-of-plane
  71.  
  72.  
  73. enum {
  74.     kDroneDrawContextSize            = 5        // Used for picking
  75. };
  76.  
  77.  
  78. typedef enum TDroneOrder {                    // In order of evaluation
  79.     kDroneOrder_Self,
  80.     kDroneOrder_Auto,
  81.     kDroneOrder_Bullet
  82. } TDroneOrder;
  83.  
  84.  
  85. typedef void (*TDroneMoveMethod)(
  86.     TDroneObject            inDrone);
  87.  
  88. typedef void (*TDroneUpdateSoundMethod)(
  89.     TDroneObject            inDrone);
  90.  
  91. typedef void (*TDroneSubmitMethod)(
  92.     TDroneObject            inDrone,
  93.     Boolean                    inHUDVisible,
  94.     TQ3ViewObject            inView);
  95.  
  96. typedef void (*TDronePickSubmitMethod)(
  97.     TDroneObject            inDrone,
  98.     TQ3ViewObject            inView);
  99.  
  100. typedef void (*TDroneHitMethod)(
  101.     TDroneObject            inDrone);
  102.  
  103.  
  104. typedef enum TAutoSound {
  105.     kAutoSound_None,
  106.     kAutoSound_Idle,
  107.     kAutoSound_Burn,
  108.     kAutoSound_Explosion
  109. } TAutoSound;
  110.  
  111. typedef enum TAutoMode {
  112.     kAutoMode_Idle,
  113.     kAutoMode_Burn,
  114.     kAutoMode_Rest,
  115.     kAutoMode_Explosion
  116. } TAutoMode;
  117.  
  118.  
  119. typedef struct TDroneData {
  120.     TThumbprint                thumbprint;            // For validation
  121.     
  122.     TDroneMoveMethod        moveMethod;            // Method: Drone_Move
  123.     TDroneUpdateSoundMethod    updateSoundMethod;    // Method: Drone_UpdateSound
  124.     TDroneSubmitMethod        submitMethod;        // Method: Drone_Submit
  125.     TDronePickSubmitMethod    pickSubmitMethod;    // Method: Drone_PickSubmit
  126.     TDroneHitMethod            hitMethod;            // Method: Drone_Hit
  127.     
  128.     TDroneObject            prev;                // The global list of drones
  129.     TDroneObject            next;
  130.     TDroneOrder                order;                // Sort key
  131.     
  132.     Boolean                    mark;                // Is this drone marked to die?
  133.     
  134.     TQ3Point3D                position;            // Current position
  135.     TQ3Point3D                position1;            // Previous position
  136.     
  137.     TQ3Vector3D                velocity;            // Change in position over time
  138.     TQ3Vector3D                velocity1;            // Previous velocity
  139.     
  140.     TQ3Vector3D                acceleration;        // Change in velocity over time
  141.     
  142.     TQ3Vector3D                direction;            // Forward        (model X axis)
  143.     TQ3Vector3D                up;                    // Vertical        (model Y axis)
  144.     TQ3Vector3D                cross;                // Horizontal    (model Z axis)
  145.     
  146.     TQ3Object                geometry;            // The shape of the drone
  147.     
  148.     SndChannelPtr            autoSndChannel;        // Auto: Sound channel
  149.     SSpSourceReference        autoSource;            // Auto: 3D sound source
  150.     TAutoSound                autoSound;            // Auto: Sound currently playing
  151.     TDroneObject            autoInterest;        // Auto: Drone that we're looking at
  152.     TQ3Vector3D                autoVelocity;        // Auto: Drone velocity (instantaneous)
  153.     TAutoMode                autoMode;            // Auto: Current state
  154.     unsigned long            autoModeTimeout;    // Auto: When does mode expire?
  155.     float                    autoDistance;        // Auto: Last distance from target
  156.     float                    autoExplosion;        // Auto: 0=start; 1=end of explosion
  157.     
  158.     TQ3Point3D                bulletOrigin;        // Bullet: Starting position of the bullet
  159. } TDroneData;
  160.  
  161.  
  162. static TDroneObject            gDroneList                        = NULL;
  163. static TQ3Object            gDroneAutoGeometry                = NULL;
  164. static TQ3Object            gDroneAutoBurnGeometry            = NULL;
  165. static TQ3Object            gDroneAutoExplosionGeometry        = NULL;
  166. static TQ3AttributeSet        gDroneBulletColor                = NULL;
  167. static TQ3ShaderObject        gDroneNULLIllumination            = NULL;
  168. static TQ3ViewObject        gDroneView                        = NULL;
  169. static TQ3DrawContextObject    gDroneDrawContext                = NULL;
  170. static TQ3CameraObject        gDroneCamera                    = NULL;
  171. static TQ3PickObject        gDronePick                        = NULL;
  172.  
  173. static SndListHandle        gDroneAutoSndIdle                = NULL;
  174. static SndListHandle        gDroneAutoSndBurn                = NULL;
  175. static SndListHandle        gDroneAutoSndExplosion            = NULL;
  176.  
  177. static long                    gDroneAutoSndIdleOffset            = 0;
  178. static long                    gDroneAutoSndBurnOffset            = 0;
  179. static long                    gDroneAutoSndExplosionOffset    = 0;
  180.  
  181. static TQ3GeometryObject    gDroneAutoMarkerEqual            = NULL;
  182. static TQ3GeometryObject    gDroneAutoMarkerAbove            = NULL;
  183. static TQ3GeometryObject    gDroneAutoMarkerBelow            = NULL;
  184.  
  185. static unsigned char        gDroneAutoMarkerDataEqual[8] =
  186.                                     {0x38, 0x44, 0x82, 0x92, 0x82, 0x44, 0x38, 0x00};
  187.  
  188. static unsigned char        gDroneAutoMarkerDataAbove[8] =
  189.                                     {0x38, 0x54, 0x92, 0xFE, 0x92, 0x54, 0x38, 0x00};
  190.  
  191. static unsigned char        gDroneAutoMarkerDataBelow[8] =
  192.                                     {0x38, 0x44, 0x82, 0xFE, 0x82, 0x44, 0x38, 0x00};
  193.  
  194. static Boolean                gDroneAutoCheckFilterVersion    = true;
  195.  
  196.  
  197. static TDroneObject Drone_New(
  198.     TDroneOrder                inOrder);
  199.  
  200. static void SelfDrone_Move(
  201.     TDroneObject            inDrone);
  202.  
  203. static void AutoDrone_Move(
  204.     TDroneObject            inDrone);
  205.  
  206. static void BulletDrone_Move(
  207.     TDroneObject            inDrone);
  208.  
  209. static void AutoDrone_UpdateSound(
  210.     TDroneObject            inDrone);
  211.  
  212. static void AutoDrone_Submit(
  213.     TDroneObject            inDrone,
  214.     Boolean                    inHUDVisible,
  215.     TQ3ViewObject            inView);
  216.  
  217. static void BulletDrone_Submit(
  218.     TDroneObject            inDrone,
  219.     Boolean                    inHUDVisible,
  220.     TQ3ViewObject            inView);
  221.  
  222. static void Drone_PickSubmit(
  223.     TDroneObject            inDrone,
  224.     TQ3ViewObject            inView);
  225.  
  226. static void AutoDrone_PickSubmit(
  227.     TDroneObject            inDrone,
  228.     TQ3ViewObject            inView);
  229.  
  230. static void Drone_Hit(
  231.     TDroneObject            inDrone);
  232.  
  233. static void AutoDrone_Hit(
  234.     TDroneObject            inDrone);
  235.  
  236. void Drone_GetMatrix(
  237.     TDroneObject            inDrone,
  238.     TQ3Matrix4x4*            outMatrix);
  239.  
  240.  
  241. /* =============================================================================
  242.  *        Drone_Init (external)
  243.  *
  244.  *    Initializes the drone stuff.
  245.  * ========================================================================== */
  246. void Drone_Init(
  247.     void)
  248. {
  249.     TQ3ColorRGB                        color;
  250.     TQ3PixmapDrawContextData        pixmapDrawContextData;
  251.     TQ3ViewAngleAspectCameraData    viewAngleCameraData;
  252.     TQ3WindowPointPickData            windowPointPickData;
  253.     TQ3MarkerData                    markerData;
  254.     
  255.     // Set up the autopilot drone geometry
  256.     gDroneAutoGeometry = Get3DMFResource(k3DMFID_AutoDrone);
  257.     assert(gDroneAutoGeometry != NULL);
  258.     
  259.     // Set up the autopilot drone burn geometry
  260.     gDroneAutoBurnGeometry = Get3DMFResource(k3DMFID_AutoDroneBurn);
  261.     assert(gDroneAutoBurnGeometry != NULL);
  262.     
  263.     // Set up the autopilot drone explosion geometry
  264.     gDroneAutoExplosionGeometry = Get3DMFResource(k3DMFID_AutoDroneExplosion);
  265.     assert(gDroneAutoExplosionGeometry != NULL);
  266.     
  267.     // Read in the autopilot drone sounds
  268.     gDroneAutoSndIdle = (SndListHandle) GetResource('snd ', kSndID_AutoIdle);
  269.     assert(gDroneAutoSndIdle != NULL);
  270.     
  271.     gDroneAutoSndBurn = (SndListHandle) GetResource('snd ', kSndID_AutoBurn);
  272.     assert(gDroneAutoSndBurn != NULL);
  273.     
  274.     gDroneAutoSndExplosion = (SndListHandle) GetResource('snd ', kSndID_AutoExplosion);
  275.     assert(gDroneAutoSndExplosion != NULL);
  276.     
  277.     GetSoundHeaderOffset(gDroneAutoSndIdle,            &gDroneAutoSndIdleOffset);
  278.     GetSoundHeaderOffset(gDroneAutoSndBurn,            &gDroneAutoSndBurnOffset);
  279.     GetSoundHeaderOffset(gDroneAutoSndExplosion,    &gDroneAutoSndExplosionOffset);
  280.     
  281.     // Set up the bullet drone line color
  282.     gDroneBulletColor = Q3AttributeSet_New();
  283.     assert(gDroneBulletColor != NULL);
  284.     
  285.     color.r = 1.0;
  286.     color.g = 0.8;
  287.     color.b = 0.1;
  288.     
  289.     Q3AttributeSet_Add(gDroneBulletColor, kQ3AttributeTypeDiffuseColor, &color);
  290.     
  291.     // Create the bullet null illum shader
  292.     gDroneNULLIllumination = Q3NULLIllumination_New();
  293.     assert(gDroneNULLIllumination != NULL);
  294.     
  295.     // Create the view that we use for bullet collision detection
  296.     gDroneView = Q3View_New();
  297.     assert(gDroneView != NULL);
  298.     
  299.     // Create its draw context
  300.     pixmapDrawContextData.drawContextData.clearImageMethod        = kQ3ClearMethodWithColor;
  301.     pixmapDrawContextData.drawContextData.clearImageColor.a        = 1.0;
  302.     pixmapDrawContextData.drawContextData.clearImageColor.r        = 0.0;
  303.     pixmapDrawContextData.drawContextData.clearImageColor.g        = 0.0;
  304.     pixmapDrawContextData.drawContextData.clearImageColor.b        = 0.0;
  305.     pixmapDrawContextData.drawContextData.paneState                = kQ3False;
  306.     pixmapDrawContextData.drawContextData.maskState                = kQ3False;
  307.     pixmapDrawContextData.drawContextData.doubleBufferState        = kQ3False;
  308.     pixmapDrawContextData.pixmap.width                            = kDroneDrawContextSize;
  309.     pixmapDrawContextData.pixmap.height                            = kDroneDrawContextSize;
  310.     pixmapDrawContextData.pixmap.rowBytes                        = pixmapDrawContextData.pixmap.width*4;
  311.     pixmapDrawContextData.pixmap.pixelSize                        = 32;
  312.     pixmapDrawContextData.pixmap.pixelType                        = kQ3PixelTypeRGB32;
  313.     pixmapDrawContextData.pixmap.bitOrder                        = kQ3EndianBig;
  314.     pixmapDrawContextData.pixmap.byteOrder                        = kQ3EndianBig;
  315.     pixmapDrawContextData.pixmap.image                            = malloc(pixmapDrawContextData.pixmap.height*pixmapDrawContextData.pixmap.rowBytes);
  316.     
  317.     gDroneDrawContext = Q3PixmapDrawContext_New(&pixmapDrawContextData);
  318.     assert(gDroneDrawContext != NULL);
  319.     
  320.     Q3View_SetDrawContext(gDroneView, gDroneDrawContext);
  321.     
  322.     // Create its camera
  323.     viewAngleCameraData.cameraData.placement.cameraLocation.x    = 0.0;
  324.     viewAngleCameraData.cameraData.placement.cameraLocation.y    = 0.0;
  325.     viewAngleCameraData.cameraData.placement.cameraLocation.z    = 0.0;
  326.     viewAngleCameraData.cameraData.placement.pointOfInterest.x    = 1.0;
  327.     viewAngleCameraData.cameraData.placement.pointOfInterest.y    = 0.0;
  328.     viewAngleCameraData.cameraData.placement.pointOfInterest.z    = 0.0;
  329.     viewAngleCameraData.cameraData.placement.upVector.x            = 0.0;
  330.     viewAngleCameraData.cameraData.placement.upVector.y            = 1.0;
  331.     viewAngleCameraData.cameraData.placement.upVector.z            = 0.0;
  332.     viewAngleCameraData.cameraData.range.hither                    = 0.1;
  333.     viewAngleCameraData.cameraData.range.yon                    = BULLET_DRONE_LIMIT;
  334.     viewAngleCameraData.cameraData.viewPort.origin.x            = -1.0;
  335.     viewAngleCameraData.cameraData.viewPort.origin.y            = 1.0;
  336.     viewAngleCameraData.cameraData.viewPort.width                = 2.0;
  337.     viewAngleCameraData.cameraData.viewPort.height                = 2.0;
  338.     viewAngleCameraData.fov                                        = 0.1;
  339.     viewAngleCameraData.aspectRatioXToY                            = pixmapDrawContextData.pixmap.width/pixmapDrawContextData.pixmap.height;
  340.  
  341.     gDroneCamera = Q3ViewAngleAspectCamera_New(&viewAngleCameraData);
  342.     assert(gDroneCamera != NULL);
  343.     
  344.     Q3View_SetCamera(gDroneView, gDroneCamera);
  345.     
  346.     // Create the pick object
  347.     windowPointPickData.data.sort                = kQ3PickSortNearToFar;
  348.     windowPointPickData.data.mask                = kQ3PickDetailMaskPickID | kQ3PickDetailMaskDistance;
  349.     windowPointPickData.data.numHitsToReturn    = kQ3ReturnAllHits;
  350.     windowPointPickData.point.x                    = 0.5*pixmapDrawContextData.pixmap.width;
  351.     windowPointPickData.point.y                    = 0.5*pixmapDrawContextData.pixmap.height;
  352.     windowPointPickData.vertexTolerance            = 0.0;
  353.     windowPointPickData.edgeTolerance            = 0.0;
  354.     
  355.     gDronePick = Q3WindowPointPick_New(&windowPointPickData);
  356.     assert(gDronePick != NULL);
  357.     
  358.     // Create the autodrone markers
  359.     markerData.location.x            = 0.0;
  360.     markerData.location.y            = 0.0;
  361.     markerData.location.z            = 0.0;
  362.     markerData.xOffset                = -3;
  363.     markerData.yOffset                = -3;
  364.     markerData.bitmap.width            = 8;
  365.     markerData.bitmap.height        = 8;
  366.     markerData.bitmap.rowBytes        = 1;
  367.     markerData.bitmap.bitOrder        = kQ3EndianBig;
  368.     markerData.markerAttributeSet    = Q3AttributeSet_New();
  369.     
  370.     assert(markerData.markerAttributeSet != NULL);
  371.     
  372.     color.r = 1.0;
  373.     color.g = 1.0;
  374.     color.b = 0.4;
  375.     
  376.     Q3AttributeSet_Add(markerData.markerAttributeSet, kQ3AttributeTypeDiffuseColor, &color);
  377.     
  378.     markerData.bitmap.image = (unsigned char*) gDroneAutoMarkerDataEqual;
  379.     gDroneAutoMarkerEqual = Q3Marker_New(&markerData);
  380.     assert(gDroneAutoMarkerEqual != NULL);
  381.     
  382.     markerData.bitmap.image = (unsigned char*) gDroneAutoMarkerDataAbove;
  383.     gDroneAutoMarkerAbove = Q3Marker_New(&markerData);
  384.     assert(gDroneAutoMarkerAbove != NULL);
  385.     
  386.     markerData.bitmap.image = (unsigned char*) gDroneAutoMarkerDataBelow;
  387.     gDroneAutoMarkerBelow = Q3Marker_New(&markerData);
  388.     assert(gDroneAutoMarkerBelow != NULL);
  389.     
  390.     Q3Object_Dispose(markerData.markerAttributeSet);
  391.     markerData.markerAttributeSet = NULL;
  392. }
  393.  
  394.  
  395. /* =============================================================================
  396.  *        Drone_Exit (external)
  397.  *
  398.  *    Prepares for exit.
  399.  * ========================================================================== */
  400. void Drone_Exit(
  401.     void)
  402. {
  403.     while (gDroneList != NULL)
  404.     {
  405.         Drone_Dispose(gDroneList);
  406.     }
  407.     
  408.     if (gDroneAutoGeometry != NULL)
  409.     {
  410.         Q3Object_Dispose(gDroneAutoGeometry);
  411.         gDroneAutoGeometry = NULL;
  412.     }
  413.     
  414.     if (gDroneAutoBurnGeometry != NULL)
  415.     {
  416.         Q3Object_Dispose(gDroneAutoBurnGeometry);
  417.         gDroneAutoBurnGeometry = NULL;
  418.     }
  419.     
  420.     if (gDroneAutoExplosionGeometry != NULL)
  421.     {
  422.         Q3Object_Dispose(gDroneAutoExplosionGeometry);
  423.         gDroneAutoExplosionGeometry = NULL;
  424.     }
  425.     
  426.     if (gDroneAutoSndIdle != NULL)
  427.     {
  428.         ReleaseResource((Handle) gDroneAutoSndIdle);
  429.         gDroneAutoSndIdle = NULL;
  430.     }
  431.     
  432.     if (gDroneAutoSndBurn != NULL)
  433.     {
  434.         ReleaseResource((Handle) gDroneAutoSndBurn);
  435.         gDroneAutoSndBurn = NULL;
  436.     }
  437.     
  438.     if (gDroneAutoSndExplosion != NULL)
  439.     {
  440.         ReleaseResource((Handle) gDroneAutoSndExplosion);
  441.         gDroneAutoSndExplosion = NULL;
  442.     }
  443.     
  444.     if (gDroneBulletColor != NULL)
  445.     {
  446.         Q3Object_Dispose(gDroneBulletColor);
  447.         gDroneBulletColor = NULL;
  448.     }
  449.     
  450.     if (gDroneNULLIllumination != NULL)
  451.     {
  452.         Q3Object_Dispose(gDroneNULLIllumination);
  453.         gDroneNULLIllumination = NULL;
  454.     }
  455.     
  456.     if (gDroneView != NULL)
  457.     {
  458.         Q3Object_Dispose(gDroneView);
  459.         gDroneView = NULL;
  460.     }
  461.     
  462.     if (gDroneDrawContext != NULL)
  463.     {
  464.         Q3Object_Dispose(gDroneDrawContext);
  465.         gDroneDrawContext = NULL;
  466.     }
  467.     
  468.     if (gDroneCamera != NULL)
  469.     {
  470.         Q3Object_Dispose(gDroneCamera);
  471.         gDroneCamera = NULL;
  472.     }
  473.     
  474.     if (gDronePick != NULL)
  475.     {
  476.         Q3Object_Dispose(gDronePick);
  477.         gDronePick = NULL;
  478.     }
  479.     
  480.     if (gDroneAutoMarkerEqual != NULL)
  481.     {
  482.         Q3Object_Dispose(gDroneAutoMarkerEqual);
  483.         gDroneAutoMarkerEqual = NULL;
  484.     }
  485.     
  486.     if (gDroneAutoMarkerAbove != NULL)
  487.     {
  488.         Q3Object_Dispose(gDroneAutoMarkerAbove);
  489.         gDroneAutoMarkerAbove = NULL;
  490.     }
  491.     
  492.     if (gDroneAutoMarkerBelow != NULL)
  493.     {
  494.         Q3Object_Dispose(gDroneAutoMarkerBelow);
  495.         gDroneAutoMarkerBelow = NULL;
  496.     }
  497. }
  498.  
  499.  
  500. /* =============================================================================
  501.  *        Drone_New (internal)
  502.  *
  503.  *    Creates a new drone.
  504.  * ========================================================================== */
  505. TDroneObject Drone_New(
  506.     TDroneOrder            inOrder)
  507. {
  508.     TDroneObject        drone;
  509.     TDroneObject        prev;
  510.     TDroneObject        next;
  511.     
  512.     // Allocate the memory
  513.     drone = (TDroneObject) malloc(sizeof(TDroneData));
  514.     assert(drone != NULL);
  515.     
  516.     drone->thumbprint = kThumbprint_Dead;  // until finished with it
  517.     
  518.     // Find where to insert it
  519.     prev = NULL;
  520.     next = gDroneList;
  521.     while (next != NULL && next->order < inOrder)
  522.     {
  523.         prev = next;
  524.         next = next->next;
  525.     }
  526.     
  527.     // Link it into the list
  528.     drone->prev = prev;
  529.     drone->next = next;
  530.     
  531.     if (prev != NULL)
  532.     {
  533.         prev->next = drone;
  534.     }
  535.     else
  536.     {
  537.         gDroneList = drone;
  538.     }
  539.     
  540.     if (next != NULL)
  541.     {
  542.         next->prev = drone;
  543.     }
  544.     
  545.     // Fill in the defaults
  546.     drone->moveMethod            = NULL;
  547.     drone->updateSoundMethod    = NULL;
  548.     drone->submitMethod            = NULL;
  549.     drone->pickSubmitMethod        = NULL;
  550.     drone->hitMethod            = NULL;
  551.     
  552.     drone->order                = inOrder;
  553.     
  554.     drone->mark                    = false;
  555.     
  556.     drone->position.x            = 0.0;
  557.     drone->position.y            = 0.0;
  558.     drone->position.z            = 0.0;
  559.     
  560.     drone->position1.x            = 0.0;
  561.     drone->position1.y            = 0.0;
  562.     drone->position1.z            = 0.0;
  563.     
  564.     drone->velocity.x            = 0.0;
  565.     drone->velocity.y            = 0.0;
  566.     drone->velocity.z            = 0.0;
  567.     
  568.     drone->velocity1.x            = 0.0;
  569.     drone->velocity1.y            = 0.0;
  570.     drone->velocity1.z            = 0.0;
  571.     
  572.     drone->acceleration.x        = 0.0;
  573.     drone->acceleration.y        = 0.0;
  574.     drone->acceleration.z        = 0.0;
  575.     
  576.     drone->direction.x            = 1.0;
  577.     drone->direction.y            = 0.0;
  578.     drone->direction.z            = 0.0;
  579.     
  580.     drone->up.x                    = 0.0;
  581.     drone->up.y                    = 1.0;
  582.     drone->up.z                    = 0.0;
  583.     
  584.     drone->cross.x                = 0.0;
  585.     drone->cross.y                = 0.0;
  586.     drone->cross.z                = 1.0;
  587.     
  588.     drone->geometry                = NULL;
  589.     
  590.     drone->autoSndChannel        = NULL;
  591.     drone->autoSource            = NULL;
  592.     drone->autoSound            = kAutoSound_None;
  593.     
  594.     drone->autoInterest            = NULL;
  595.     
  596.     drone->autoVelocity.x        = 0.0;
  597.     drone->autoVelocity.y        = 0.0;
  598.     drone->autoVelocity.z        = 0.0;
  599.     
  600.     drone->autoMode                = kAutoMode_Idle;
  601.     drone->autoModeTimeout        = 0;
  602.     drone->autoDistance            = 0.0;
  603.     drone->autoExplosion        = 0.0;
  604.     
  605.     drone->bulletOrigin.x        = 0.0;
  606.     drone->bulletOrigin.y        = 0.0;
  607.     drone->bulletOrigin.z        = 0.0;
  608.     
  609.     return drone;
  610. }
  611.  
  612.  
  613. /* =============================================================================
  614.  *        SelfDrone_New (external)
  615.  *
  616.  *    Creates a new drone whose movement pattern is defined by user controls.
  617.  * ========================================================================== */
  618. TDroneObject SelfDrone_New(
  619.     void)
  620. {
  621.     TDroneObject        drone;
  622.     
  623.     // Create the basic drone
  624.     drone = Drone_New(kDroneOrder_Self);
  625.     assert(drone != NULL);
  626.     
  627.     // Fill in the fields
  628.     drone->moveMethod        = SelfDrone_Move;
  629.     
  630.     // Validate it
  631.     drone->thumbprint        = kThumbprint_SelfDrone;
  632.     
  633.     return drone;
  634. }
  635.  
  636.  
  637. /* =============================================================================
  638.  *        AutoDrone_New (external)
  639.  *
  640.  *    Creates a new drone whose movement pattern is under automatic control.
  641.  * ========================================================================== */
  642. TDroneObject AutoDrone_New(
  643.     TDroneObject            inDroneOfInterest)
  644. {
  645.     TDroneObject            drone;
  646.     TQ3Vector3D                orientation;
  647.     SoundComponentLink        link;
  648.     SSpFilterVersionData    filterVersion;
  649.     
  650.     assert(inDroneOfInterest != NULL && IS_DRONE(inDroneOfInterest));
  651.     
  652.     // Create the basic drone
  653.     drone = Drone_New(kDroneOrder_Auto);
  654.     assert(drone != NULL);
  655.     
  656.     // Fill in the fields
  657.     drone->moveMethod        = AutoDrone_Move;
  658.     drone->updateSoundMethod = AutoDrone_UpdateSound;
  659.     drone->submitMethod        = AutoDrone_Submit;
  660.     drone->pickSubmitMethod    = AutoDrone_PickSubmit;
  661.     drone->hitMethod        = AutoDrone_Hit;
  662.     
  663.     // Allocate the sound channel and set up for 3D localized sound
  664.     drone->autoSndChannel    = NULL;
  665.     SndNewChannel(&drone->autoSndChannel, sampledSynth, initMono, NULL);
  666.     assert(drone->autoSndChannel != NULL);
  667.     
  668.     SSpSource_New(&drone->autoSource);
  669.     assert(drone->autoSource != NULL);
  670.     
  671.     link.description.componentType            = kSoundEffectsType;
  672.     link.description.componentSubType        = kSSpLocalizationSubType;
  673.     link.description.componentManufacturer    = 0;
  674.     link.description.componentFlags            = 0;        
  675.     link.description.componentFlagsMask        = 0;    
  676.     link.mixerID                            = nil;
  677.     link.linkID                                = nil;
  678.     
  679.     SndSetInfo(drone->autoSndChannel, siPreMixerSoundComponent, &link);
  680.     
  681.     // Verify that the right version filter was installed
  682.     if (gDroneAutoCheckFilterVersion)
  683.     {
  684.         filterVersion.manufacturer = 0;
  685.         SndGetInfo(drone->autoSndChannel, siSSpFilterVersion, &filterVersion);
  686.         
  687.         switch (filterVersion.manufacturer)
  688.         {
  689.             case 0:
  690.                 StopAlert(kAlrtID_FilterNotInstalled, NULL);
  691.                 //• TODO: Should quit or disable 3D sound
  692.             break;
  693.             
  694.             case kAppleManufacturer:
  695.                 if (!CheckVersionNumber(&filterVersion.version, 1, 0, 0))
  696.                 {
  697.                     StopAlert(kAlrtID_FilterVersion, NULL);
  698.                     //• TODO: Should quit or disable 3D sound
  699.                 }
  700.             break;
  701.         }
  702.         
  703.         gDroneAutoCheckFilterVersion = false;
  704.     }
  705.  
  706.     // The sound is loudest out the back of the model
  707.     Q3Vector3D_Set(&orientation, -1.0, 0.0, 0.0);
  708.     SSpSource_SetOrientation(drone->autoSource, &orientation);
  709.     
  710.     drone->autoInterest        = inDroneOfInterest;
  711.     drone->geometry            = Q3Shared_GetReference(gDroneAutoGeometry);
  712.     
  713.     drone->position.x        += Random()*0.0001 + AUTO_DRONE_LEAD;
  714.     drone->position.y        += Random()*0.0001;
  715.     drone->position.z        += Random()*0.0001;
  716.     
  717.     drone->autoVelocity.x    += Random()*0.0001;
  718.     drone->autoVelocity.y    += Random()*0.0001;
  719.     drone->autoVelocity.z    += Random()*0.0001;
  720.     
  721.     // Validate it
  722.     drone->thumbprint        = kThumbprint_AutoDrone;
  723.     
  724.     return drone;
  725. }
  726.  
  727.  
  728. /* =============================================================================
  729.  *        BulletDrone_New (external)
  730.  *
  731.  *    Creates a new drone whose behavior is projectile.
  732.  * ========================================================================== */
  733. TDroneObject BulletDrone_New(
  734.     const TQ3Point3D*        inPosition,
  735.     const TQ3Vector3D*        inDirection)
  736. {
  737.     TDroneObject            drone;
  738.     
  739.     assert(inPosition != NULL);
  740.     assert(inDirection != NULL);
  741.     
  742.     // Create the basic drone
  743.     drone = Drone_New(kDroneOrder_Bullet);
  744.     assert(drone != NULL);
  745.     
  746.     // Fill in the fields
  747.     drone->moveMethod        = BulletDrone_Move;
  748.     drone->submitMethod        = BulletDrone_Submit;
  749.     
  750.     drone->position            = *inPosition;
  751.     drone->direction        = *inDirection;
  752.     
  753.     drone->bulletOrigin        = *inPosition;
  754.     
  755.     // Validate it
  756.     drone->thumbprint        = kThumbprint_BulletDrone;
  757.     
  758.     return drone;
  759. }
  760.  
  761.  
  762. /* =============================================================================
  763.  *        Drone_Dispose (external)
  764.  *
  765.  *    Disposes of the drone.
  766.  * ========================================================================== */
  767. void Drone_Dispose(
  768.     TDroneObject            inDrone)
  769. {
  770.     assert(inDrone != NULL && IS_DRONE(inDrone));
  771.     
  772.     // Unlink it from the list
  773.     if (inDrone->prev != NULL)
  774.     {
  775.         inDrone->prev->next = inDrone->next;
  776.     }
  777.     else
  778.     {
  779.         gDroneList = inDrone->next;
  780.     }
  781.     
  782.     if (inDrone->next != NULL)
  783.     {
  784.         inDrone->next->prev = inDrone->prev;
  785.     }
  786.     
  787.     // Dispose stuff
  788.     if (inDrone->geometry != NULL)
  789.     {
  790.         Q3Object_Dispose(inDrone->geometry);
  791.         inDrone->geometry = NULL;
  792.     }
  793.     
  794.     // Free the sound channel
  795.     //• This rightly belongs in AutoDrone_Dispose
  796.     if (inDrone->autoSource != NULL)
  797.     {
  798.         SSpSource_Dispose(inDrone->autoSource);
  799.         inDrone->autoSource = NULL;
  800.     }
  801.     
  802.     if (inDrone->autoSndChannel != NULL)
  803.     {
  804.         SndDisposeChannel(inDrone->autoSndChannel, true);
  805.         inDrone->autoSndChannel = NULL;
  806.     }
  807.     
  808.     // Dispose of the memory
  809.     inDrone->thumbprint = kThumbprint_Dead;
  810.     free(inDrone);
  811. }
  812.  
  813.  
  814. /* =============================================================================
  815.  *        Drone_Next (external)
  816.  *
  817.  *    If inDrone is NULL, then the head of the drone list is returned.  If inDrone
  818.  *    is non-NULL, then the next drone in the list is returned.  If inDrone is the
  819.  *    last drone, then NULL is returned.
  820.  * ========================================================================== */
  821. TDroneObject Drone_Next(
  822.     TDroneObject            inDrone)
  823. {
  824.     TDroneObject            result;
  825.     
  826.     if (inDrone != NULL)
  827.     {
  828.         assert(IS_DRONE(inDrone));
  829.         
  830.         result = inDrone->next;
  831.     }
  832.     else
  833.     {
  834.         result = gDroneList;
  835.     }
  836.     
  837.     return result;
  838. }
  839.  
  840.  
  841. /* =============================================================================
  842.  *        Drone_Move (external)
  843.  *
  844.  *    Moves the drone forward one time step.  This may mark the drone for death.
  845.  * ========================================================================== */
  846. void Drone_Move(
  847.     TDroneObject            inDrone)
  848. {
  849.     assert(inDrone != NULL && IS_DRONE(inDrone));
  850.     
  851.     // Move the drone by its own rules
  852.     assert(inDrone->moveMethod != NULL);
  853.     (inDrone->moveMethod)(inDrone);
  854.     
  855.     // Drone is still alive -- compute the velocity and acceleration
  856.     Q3Point3D_Subtract(&inDrone->position, &inDrone->position1, &inDrone->velocity);
  857.     Q3Vector3D_Scale(&inDrone->velocity, gGameFramesPerSecond, &inDrone->velocity);
  858.     inDrone->position1 = inDrone->position;
  859.     
  860.     Q3Vector3D_Subtract(&inDrone->velocity, &inDrone->velocity1, &inDrone->acceleration);
  861.     Q3Vector3D_Scale(&inDrone->acceleration, gGameFramesPerSecond, &inDrone->acceleration);
  862.     inDrone->velocity1 = inDrone->velocity;
  863.     
  864.     // Reorthogonalize the up and cross vectors
  865.     assert(fabs(Q3Vector3D_Length(&inDrone->direction) - 1.0) < 0.05);
  866.     
  867.     Q3Vector3D_Cross(&inDrone->direction, &inDrone->up, &inDrone->cross);
  868.     Q3Vector3D_Normalize(&inDrone->cross, &inDrone->cross);
  869.     
  870.     Q3Vector3D_Cross(&inDrone->cross, &inDrone->direction, &inDrone->up);
  871. }
  872.  
  873.  
  874. /* =============================================================================
  875.  *        SelfDrone_Move (internal)
  876.  *
  877.  *    Moves the drone forward one time step.
  878.  * ========================================================================== */
  879. void SelfDrone_Move(
  880.     TDroneObject            inDrone)
  881. {
  882.     TQ3Vector3D                v;
  883.     
  884.     assert(inDrone != NULL && IS_SELF_DRONE(inDrone));
  885.     
  886.     Q3Vector3D_Scale(&inDrone->direction, gGameInterval*SELF_DRONE_SPEED, &v);
  887.     Q3Point3D_Vector3D_Add(&inDrone->position, &v, &inDrone->position);
  888. }
  889.  
  890.  
  891. /* =============================================================================
  892.  *        AutoDrone_Move (internal)
  893.  *
  894.  *    Moves the drone forward one time step.  The autopilot drone has a constant
  895.  *    thrust along its direction of orientation.  The direction is controlled to
  896.  *    point just in front of the drone of interest.
  897.  * ========================================================================== */
  898. void AutoDrone_Move(
  899.     TDroneObject            inDrone)
  900. {
  901.     TQ3Vector3D                newDirection;
  902.     TQ3Vector3D                v1;
  903.     TQ3Vector3D                v2;
  904.     TQ3Point3D                target;
  905.     float                    distance;
  906.     float                    turnRate;
  907.     float                    limit;
  908.     TAutoSound                newSound;
  909.     SndCommand                sndCommand;
  910.     long                    base;
  911.     TQ3Matrix4x4            matrix;
  912.     
  913.     assert(inDrone != NULL && IS_AUTO_DRONE(inDrone));
  914.     
  915.     // Figure out new direction
  916.     if (inDrone->autoMode == kAutoMode_Explosion)
  917.     {
  918.         // Point the explosion geometry at the camera
  919.         //• NOTE: For now, we assume that the camera is at the drone of interest
  920.         //• This is wrong for two reasons.  First, the camera could actually be
  921.         //• elsewhere.  Second, this assumes that the drone of interest moves
  922.         //• before this drone.
  923.         Q3Point3D_Subtract(
  924.                 &inDrone->autoInterest->position,
  925.                 &inDrone->position,
  926.                 &newDirection);
  927.         
  928.         Q3Vector3D_Normalize(&newDirection, &inDrone->direction);
  929.     }
  930.     else
  931.     {
  932.         // Find a point in front of the drone of interest
  933.         assert(IS_DRONE(inDrone->autoInterest));
  934.         
  935.         Q3Vector3D_Scale(&inDrone->autoInterest->direction, AUTO_DRONE_LEAD, &v1);
  936.         Q3Point3D_Vector3D_Add(&inDrone->autoInterest->position, &v1, &target);
  937.         
  938.         // Point toward the target
  939.         Q3Point3D_Subtract(&target, &inDrone->position, &newDirection);
  940.         Q3Vector3D_Normalize(&newDirection, &newDirection);
  941.         
  942.         // Limit the turn rate
  943.         turnRate = acosf(Q3Vector3D_Dot(&inDrone->direction, &newDirection));
  944.         limit = AUTO_DRONE_TURN_RATE*gGameInterval;
  945.         if (turnRate > limit)
  946.         {
  947.             // Limit the turn
  948.             // Note: this should actually be spherical interpolation -- but linear is close enough
  949.             turnRate = limit/turnRate;
  950.             Q3Vector3D_Scale(&inDrone->direction, 1.0-turnRate, &v1);
  951.             Q3Vector3D_Scale(&newDirection, turnRate, &v2);
  952.             Q3Vector3D_Add(&v1, &v2, &inDrone->direction);
  953.             Q3Vector3D_Normalize(&inDrone->direction, &inDrone->direction);
  954.         }
  955.         else
  956.         {
  957.             // It's OK to make the desired turn
  958.             inDrone->direction = newDirection;
  959.         }
  960.     }
  961.     
  962.     // Figure out new position
  963.     switch (inDrone->autoMode)
  964.     {
  965.         case kAutoMode_Idle: // Start a burn if we are getting farther from the target
  966.             // Start burn if we are getting farther from the target
  967.             distance = Q3Point3D_Distance(&inDrone->position, &target);
  968.             if (inDrone->autoDistance > distance)
  969.             {
  970.                 // Getting closer to target -- don't change
  971.                 inDrone->autoDistance = distance;
  972.             }
  973.             else
  974.             {
  975.                 // Getting farther from target -- start a burn
  976.                 inDrone->autoMode = kAutoMode_Burn;
  977.                 inDrone->autoModeTimeout = TickCount() + (unsigned long) (AUTO_DRONE_BURN_TIME*60);
  978.             }
  979.         break;
  980.         
  981.         case kAutoMode_Burn: // Accelerate toward the target
  982.             // Continue the burn?
  983.             if (TickCount() <= inDrone->autoModeTimeout)
  984.             {
  985.                 // Still burning -- change the velocity by accelerating toward the target
  986.                 Q3Vector3D_Scale(&inDrone->direction, gGameInterval*AUTO_DRONE_ACCEL, &v1);
  987.                 Q3Vector3D_Add(&inDrone->autoVelocity, &v1, &inDrone->autoVelocity);
  988.             }
  989.             else
  990.             {
  991.                 // Switch to rest mode
  992.                 inDrone->autoMode = kAutoMode_Rest;
  993.                 inDrone->autoModeTimeout = TickCount() + (unsigned long) (AUTO_DRONE_REST_TIME*60);
  994.             }
  995.         break;
  996.         
  997.         case kAutoMode_Rest: // Don't use the engine for a while
  998.             // Continue the rest?
  999.             if (TickCount() <= inDrone->autoModeTimeout)
  1000.             {
  1001.                 // Still resting
  1002.                 // (do nothing)
  1003.             }
  1004.             else
  1005.             {
  1006.                 // Switch to idle mode
  1007.                 inDrone->autoMode = kAutoMode_Idle;
  1008.                 inDrone->autoDistance = Q3Point3D_Distance(&inDrone->position, &target);
  1009.             }
  1010.         break;
  1011.         
  1012.         case kAutoMode_Explosion: // Show the explosion for a while
  1013.             // Continue the explosion?
  1014.             inDrone->autoExplosion += gGameInterval/AUTO_DRONE_EXPLOSION_TIME;
  1015.             if (inDrone->autoExplosion <= 1.0)
  1016.             {
  1017.                 // Still exploding
  1018.                 inDrone->autoVelocity.x =
  1019.                 inDrone->autoVelocity.y =
  1020.                 inDrone->autoVelocity.z = 0.0;
  1021.             }
  1022.             else
  1023.             {
  1024.                 // Kill the drone
  1025.                 Drone_SetMark(inDrone, true);
  1026.             }
  1027.         break;
  1028.         
  1029.         default:
  1030.             assert(0);
  1031.     }
  1032.     
  1033.     // Move along the new velocity vector
  1034.     Q3Vector3D_Scale(&inDrone->autoVelocity, gGameInterval, &v1);
  1035.     Q3Point3D_Vector3D_Add(&inDrone->position, &v1, &inDrone->position);
  1036.     
  1037.     // Choose the next sound to play
  1038.     switch (inDrone->autoMode)
  1039.     {
  1040.         case kAutoMode_Idle:
  1041.         case kAutoMode_Rest:
  1042.             newSound = kAutoSound_Idle;
  1043.         break;
  1044.         
  1045.         case kAutoMode_Burn:
  1046.             newSound = kAutoSound_Burn;
  1047.         break;
  1048.         
  1049.         break;
  1050.         
  1051.         case kAutoMode_Explosion:
  1052.             newSound = kAutoSound_Explosion;
  1053.         break;
  1054.         
  1055.         default:
  1056.             assert(0);
  1057.     }
  1058.     
  1059.     // Change the sound
  1060.     if (inDrone->autoSound != newSound)
  1061.     {
  1062.         // Stop the old sound
  1063.         if (inDrone->autoSound != kAutoSound_None)
  1064.         {
  1065.             sndCommand.cmd = quietCmd;
  1066.             sndCommand.param1 = 0;
  1067.             sndCommand.param2 = 0;
  1068.             SndDoImmediate(inDrone->autoSndChannel, &sndCommand);
  1069.         }
  1070.         
  1071.         inDrone->autoSound = newSound;
  1072.         
  1073.         // Play the new sound
  1074.         switch (inDrone->autoSound)
  1075.         {
  1076.             case kAutoSound_None:
  1077.                 base = 0;
  1078.             break;
  1079.             
  1080.             case kAutoSound_Idle:
  1081.                 base = (long) *gDroneAutoSndIdle + gDroneAutoSndIdleOffset;
  1082.                 SSpSource_SetAngularAttenuation(inDrone->autoSource, 0.0, 0.0);
  1083.                 SSpSource_SetReferenceDistance(inDrone->autoSource, AUTO_DRONE_IDLE_REF_DIST);
  1084.             break;
  1085.             
  1086.             case kAutoSound_Burn:
  1087.                 base = (long) *gDroneAutoSndBurn + gDroneAutoSndBurnOffset;
  1088.                 SSpSource_SetAngularAttenuation(inDrone->autoSource, 1.5, -12.0);
  1089.                 SSpSource_SetReferenceDistance(inDrone->autoSource, AUTO_DRONE_BURN_REF_DIST);
  1090.             break;
  1091.             
  1092.             case kAutoSound_Explosion:
  1093.                 base = (long) *gDroneAutoSndExplosion + gDroneAutoSndExplosionOffset;
  1094.                 SSpSource_SetAngularAttenuation(inDrone->autoSource, 0.0, 0.0);
  1095.                 SSpSource_SetReferenceDistance(inDrone->autoSource, AUTO_DRONE_EXPL_REF_DIST);
  1096.             break;
  1097.             
  1098.             default:
  1099.                 assert(0);
  1100.         }
  1101.         
  1102.         if (base != 0)
  1103.         {
  1104.             // Install the sound
  1105.             sndCommand.cmd = soundCmd;
  1106.             sndCommand.param1 = 0;
  1107.             sndCommand.param2 = base;
  1108.             SndDoImmediate(inDrone->autoSndChannel, &sndCommand);
  1109.             
  1110.             // Play it indefinitely
  1111.             sndCommand.cmd = freqCmd;
  1112.             sndCommand.param1 = 0;
  1113.             sndCommand.param2 = 60;
  1114.             SndDoImmediate(inDrone->autoSndChannel, &sndCommand);
  1115.         }
  1116.     }
  1117.     
  1118.     // Change the sound source location
  1119.     Drone_GetMatrix(inDrone, &matrix);
  1120.     SSpSource_SetTransform(inDrone->autoSource, &matrix);
  1121. }
  1122.  
  1123.  
  1124. /* =============================================================================
  1125.  *        BulletDrone_Move (internal)
  1126.  *
  1127.  *    Moves the drone forward one time step.
  1128.  * ========================================================================== */
  1129. void BulletDrone_Move(
  1130.     TDroneObject            inDrone)
  1131. {
  1132.     TQ3Vector3D                v;
  1133.     float                    prevDistance;
  1134.     float                    currDistance;
  1135.     TQ3CameraPlacement        placement;
  1136.     TDroneObject            target;
  1137.     unsigned long            count;
  1138.     unsigned long            index;
  1139.     TQ3HitData                hitData;
  1140.     
  1141.     assert(inDrone != NULL && IS_BULLET_DRONE(inDrone));
  1142.     
  1143.     // Move the bullet
  1144.     prevDistance = Q3Point3D_Distance(&inDrone->position, &inDrone->bulletOrigin);
  1145.     
  1146.     Q3Vector3D_Scale(&inDrone->direction, gGameInterval*BULLET_DRONE_SPEED, &v);
  1147.     Q3Point3D_Vector3D_Add(&inDrone->position, &v, &inDrone->position);
  1148.     
  1149.     currDistance = Q3Point3D_Distance(&inDrone->position, &inDrone->bulletOrigin);
  1150.     
  1151.     // Time to expire?
  1152.     if (currDistance > BULLET_DRONE_LIMIT)
  1153.     {
  1154.         // Mark the drone to die
  1155.         Drone_SetMark(inDrone, true);
  1156.     }
  1157.     else
  1158.     {
  1159.         // Set up for collision detection
  1160.         placement.cameraLocation    = inDrone->bulletOrigin;
  1161.         placement.upVector            = inDrone->up;
  1162.         
  1163.         Q3Point3D_Vector3D_Add(&inDrone->bulletOrigin, &inDrone->direction, &placement.pointOfInterest);
  1164.         
  1165.         Q3Camera_SetPlacement(gDroneCamera, &placement);
  1166.         
  1167.         // Collision detection with all target drones
  1168.         Q3View_StartPicking(gDroneView, gDronePick);
  1169.         do
  1170.         {
  1171.             for (target = Drone_Next(NULL); target != NULL; target = Drone_Next(target))
  1172.             {
  1173.                 // Submit the drone geometry, along with a PickID that is the object reference
  1174.                 Q3PickIDStyle_Submit((unsigned long) target, gDroneView);
  1175.                 Drone_PickSubmit(target, gDroneView);
  1176.             }
  1177.         }
  1178.         while (Q3View_EndPicking(gDroneView) == kQ3ViewStatusRetraverse);
  1179.         
  1180.         // Check the hit list
  1181.         Q3Pick_GetNumHits(gDronePick, &count);
  1182.         for (index = 0; index < count; index++)
  1183.         {
  1184.             Q3Pick_GetHitData(gDronePick, index, &hitData);
  1185.             
  1186.             if ((hitData.validMask & kQ3PickDetailMaskPickID) &&
  1187.                 (hitData.validMask & kQ3PickDetailMaskDistance))
  1188.             {
  1189.                 target = (TDroneObject) hitData.pickID;
  1190.                 
  1191.                 if (target != NULL && IS_DRONE(target))
  1192.                 {
  1193.                     // Got a valid hit -- check its range
  1194.                     //• Should it be a bullet or a laser?
  1195.                     if ((1 || prevDistance <= hitData.distance) && hitData.distance <= currDistance)
  1196.                     {
  1197.                         // Hit it!
  1198.                         Drone_Hit(target);
  1199.                         
  1200.                         // Kill the bullet
  1201.                         Drone_SetMark(inDrone, true);
  1202.                         break;
  1203.                     }
  1204.                 }
  1205.             }
  1206.             
  1207.             Q3Hit_EmptyData(&hitData);
  1208.         }
  1209.         
  1210.         // Empty out the pick hits
  1211.         Q3Pick_EmptyHitList(gDronePick);
  1212.     }
  1213. }
  1214.  
  1215.  
  1216. /* =============================================================================
  1217.  *        Drone_UpdateSound (external)
  1218.  *
  1219.  *    Updates localized sounds for this drone.  The default method does nothing.
  1220.  * ========================================================================== */
  1221. void Drone_UpdateSound(
  1222.     TDroneObject            inDrone)
  1223. {
  1224.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1225.     
  1226.     if (inDrone->updateSoundMethod != NULL)
  1227.     {
  1228.         (inDrone->updateSoundMethod)(inDrone);
  1229.     }
  1230. }
  1231.  
  1232.  
  1233. /* =============================================================================
  1234.  *        AutoDrone_UpdateSound (external)
  1235.  *
  1236.  *    Updates localized sounds for this autopilot drone.
  1237.  * ========================================================================== */
  1238. void AutoDrone_UpdateSound(
  1239.     TDroneObject            inDrone)
  1240. {
  1241.     SSpLocalizationData                snd3DInfo;
  1242.     
  1243.     assert(inDrone != NULL && IS_AUTO_DRONE(inDrone));
  1244.     
  1245.     SSpSource_CalcLocalization(inDrone->autoSource, Sound_GetListener(), &snd3DInfo);
  1246.     
  1247.     SndSetInfo(inDrone->autoSndChannel, siSSpLocalization, &snd3DInfo);
  1248. }
  1249.  
  1250.  
  1251. /* =============================================================================
  1252.  *        Drone_Submit (external)
  1253.  *
  1254.  *    Submits the drone for drawing.
  1255.  * ========================================================================== */
  1256. void Drone_Submit(
  1257.     TDroneObject            inDrone,
  1258.     Boolean                    inHUDVisible,
  1259.     TQ3ViewObject            inView)
  1260. {
  1261.     TQ3Matrix4x4            matrix;
  1262.     
  1263.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1264.     
  1265.     if (inDrone->submitMethod != NULL)
  1266.     {
  1267.         // Use the submit method
  1268.         (inDrone->submitMethod)(inDrone, inHUDVisible, inView);
  1269.     }
  1270.     else if (inDrone->geometry != NULL)
  1271.     {
  1272.         // Submit the geometry
  1273.         Drone_GetMatrix(inDrone, &matrix);
  1274.         
  1275.         Q3Push_Submit(inView);
  1276.         Q3MatrixTransform_Submit(&matrix, inView);
  1277.         Q3Object_Submit(inDrone->geometry, inView);
  1278.         Q3Pop_Submit(inView);
  1279.     }
  1280. }
  1281.  
  1282.  
  1283. /* =============================================================================
  1284.  *        AutoDrone_Submit (internal)
  1285.  *
  1286.  *    Submits the bullet drone for drawing.
  1287.  * ========================================================================== */
  1288. void AutoDrone_Submit(
  1289.     TDroneObject            inDrone,
  1290.     Boolean                    inHUDVisible,
  1291.     TQ3ViewObject            inView)
  1292. {
  1293.     TQ3Matrix4x4            matrix;
  1294.     TQ3ColorRGB                transparency;
  1295.     TQ3Point3D                position;
  1296.     TQ3Vector3D                direction;
  1297.     TQ3Vector3D                up;
  1298.     TQ3Vector3D                right;
  1299.     TQ3Vector3D                v;
  1300.     TQ3Vector3D                v1;
  1301.     TQ3Vector3D                v2;
  1302.     float                    height;
  1303.     TQ3GeometryObject        marker;
  1304.     
  1305.     assert(inDrone != NULL && IS_AUTO_DRONE(inDrone));
  1306.     assert(inView != NULL);
  1307.     
  1308.     // Draw the drone
  1309.     Q3Push_Submit(inView);
  1310.     
  1311.     Drone_GetMatrix(inDrone, &matrix);
  1312.     Q3MatrixTransform_Submit(&matrix, inView);
  1313.     
  1314.     switch (inDrone->autoMode)
  1315.     {
  1316.         case kAutoMode_Burn:
  1317.             Q3Object_Submit(gDroneAutoBurnGeometry, inView);
  1318.             /* FALL THROUGH TO SUBMIT DRONE GEOMETRY */
  1319.         
  1320.         case kAutoMode_Rest:
  1321.         case kAutoMode_Idle:
  1322.             Q3Object_Submit(inDrone->geometry, inView);
  1323.         break;
  1324.         
  1325.         case kAutoMode_Explosion:
  1326.             // Grow the explosion
  1327.             v.x = v.y = v.z = (1.0-AUTO_DRONE_MIN_SCALE)*inDrone->autoExplosion + AUTO_DRONE_MIN_SCALE;
  1328.             Q3ScaleTransform_Submit(&v, inView);
  1329.             
  1330.             // Fade at the end
  1331.             if (inDrone->autoExplosion > (1.0-AUTO_DRONE_FADE_TIME))
  1332.             {
  1333.                 transparency.r =
  1334.                 transparency.g =
  1335.                 transparency.b = (-1.0/AUTO_DRONE_FADE_TIME)*inDrone->autoExplosion + (1.0/AUTO_DRONE_FADE_TIME);
  1336.                 
  1337.                 Q3Attribute_Submit(kQ3AttributeTypeTransparencyColor, &transparency, inView);
  1338.             }
  1339.             
  1340.             // Submit the explosion geometry
  1341.             Q3Object_Submit(gDroneAutoExplosionGeometry, inView);
  1342.         break;
  1343.         
  1344.         default:
  1345.             assert(0);
  1346.     }
  1347.     
  1348.     Q3Pop_Submit(inView);
  1349.     
  1350.     // Draw the HUD marker for the drone
  1351.     if (inHUDVisible && inDrone->autoMode != kAutoMode_Explosion)
  1352.     {
  1353.         Display_GetViewerPosition(&position, &direction, &up);
  1354.         Q3Vector3D_Cross(&direction, &up, &right);
  1355.         
  1356.         Q3Point3D_Subtract(&inDrone->position, &position, &v);
  1357.         
  1358.         Q3Vector3D_Scale(&up,    HUD_SCALE*Q3Vector3D_Dot(&direction, &v), &v1);
  1359.         Q3Vector3D_Scale(&right, HUD_SCALE*Q3Vector3D_Dot(&right,     &v), &v2);
  1360.         
  1361.         Q3Point3D_Vector3D_Add(&position, &direction, &position);
  1362.         Q3Point3D_Vector3D_Add(&position, &v1,        &position);
  1363.         Q3Point3D_Vector3D_Add(&position, &v2,        &position);
  1364.         
  1365.         Q3Vector3D_Normalize(&v, &v);
  1366.         height = Q3Vector3D_Dot(&up, &v);
  1367.         if (height >= HUD_HEIGHT)
  1368.         {
  1369.             marker = gDroneAutoMarkerAbove;
  1370.         }
  1371.         else if (height <= -HUD_HEIGHT)
  1372.         {
  1373.             marker = gDroneAutoMarkerBelow;
  1374.         }
  1375.         else
  1376.         {
  1377.             marker = gDroneAutoMarkerEqual;
  1378.         }
  1379.         
  1380.         Q3Marker_SetPosition(marker, &position);
  1381.         Q3Object_Submit(marker, inView);
  1382.     }
  1383. }
  1384.  
  1385.  
  1386. /* =============================================================================
  1387.  *        BulletDrone_Submit (internal)
  1388.  *
  1389.  *    Submits the bullet drone for drawing.
  1390.  * ========================================================================== */
  1391. void BulletDrone_Submit(
  1392.     TDroneObject            inDrone,
  1393.     Boolean                    inHUDVisible,
  1394.     TQ3ViewObject            inView)
  1395. {
  1396.     TQ3LineData                lineData;
  1397.     
  1398.     assert(inDrone != NULL && IS_BULLET_DRONE(inDrone));
  1399.     assert(inView != NULL);
  1400.     
  1401.     lineData.vertices[0].point            = inDrone->bulletOrigin;
  1402.     lineData.vertices[0].attributeSet    = NULL;
  1403.     lineData.vertices[1].point            = inDrone->position;
  1404.     lineData.vertices[1].attributeSet    = NULL;
  1405.     lineData.lineAttributeSet            = NULL;
  1406.     
  1407.     Q3Push_Submit(inView);
  1408.     Q3Object_Submit(gDroneNULLIllumination, inView);
  1409.     Q3Object_Submit(gDroneBulletColor, inView);
  1410.     Q3Line_Submit(&lineData, inView);
  1411.     Q3Pop_Submit(inView);
  1412. }
  1413.  
  1414.  
  1415. /* =============================================================================
  1416.  *        Drone_PickSubmit (internal)
  1417.  *
  1418.  *    Submits the drone for picking against a bullet.  It forwards to the actual
  1419.  *    drone hit method, if any.
  1420.  * ========================================================================== */
  1421. void Drone_PickSubmit(
  1422.     TDroneObject            inDrone,
  1423.     TQ3ViewObject            inView)
  1424. {
  1425.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1426.     
  1427.     if (inDrone->pickSubmitMethod != NULL)
  1428.     {
  1429.         (*inDrone->pickSubmitMethod)(inDrone, inView);
  1430.     }
  1431. }
  1432.  
  1433.  
  1434. /* =============================================================================
  1435.  *        AutoDrone_PickSubmit (internal)
  1436.  *
  1437.  *    Submits the autopilot drone for picking against a bullet.
  1438.  * ========================================================================== */
  1439. void AutoDrone_PickSubmit(
  1440.     TDroneObject            inDrone,
  1441.     TQ3ViewObject            inView)
  1442. {
  1443.     TQ3Matrix4x4            matrix;
  1444.     
  1445.     assert(inDrone != NULL && IS_AUTO_DRONE(inDrone));
  1446.     assert(inView != NULL);
  1447.     
  1448.     // Draw the drone
  1449.     if (inDrone->autoMode != kAutoMode_Explosion)
  1450.     {
  1451.         Q3Push_Submit(inView);
  1452.         
  1453.         Drone_GetMatrix(inDrone, &matrix);
  1454.         Q3MatrixTransform_Submit(&matrix, inView);
  1455.         
  1456.         Q3Object_Submit(gDroneAutoBurnGeometry, inView);
  1457.         
  1458.         Q3Pop_Submit(inView);
  1459.     }
  1460. }
  1461.  
  1462.  
  1463. /* =============================================================================
  1464.  *        Drone_Hit (internal)
  1465.  *
  1466.  *    Called when this drone is hit.  It forwards to the actual drone hit method,
  1467.  *    if any.
  1468.  * ========================================================================== */
  1469. void Drone_Hit(
  1470.     TDroneObject            inDrone)
  1471. {
  1472.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1473.     
  1474.     if (inDrone->hitMethod != NULL)
  1475.     {
  1476.         (*inDrone->hitMethod)(inDrone);
  1477.     }
  1478. }
  1479.  
  1480.  
  1481. /* =============================================================================
  1482.  *        AutoDrone_Hit (internal)
  1483.  *
  1484.  *    Called when this autopilot drone is hit.  It puts the drone into
  1485.  *    explosion mode.
  1486.  * ========================================================================== */
  1487. void AutoDrone_Hit(
  1488.     TDroneObject            inDrone)
  1489. {
  1490.     assert(inDrone != NULL && IS_AUTO_DRONE(inDrone));
  1491.     
  1492.     inDrone->autoMode = kAutoMode_Explosion;
  1493.     inDrone->autoExplosion = 0.0;
  1494. }
  1495.  
  1496.  
  1497. /* =============================================================================
  1498.  *        Drone_SetMark (external)
  1499.  *
  1500.  *    Changes the drone's mark to the given value.  The mark is used to indicate
  1501.  *    which drones should die.
  1502.  * ========================================================================== */
  1503. void Drone_SetMark(
  1504.     TDroneObject            inDrone,
  1505.     Boolean                    inMark)
  1506. {
  1507.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1508.     
  1509.     inDrone->mark = inMark;
  1510. }
  1511.  
  1512.  
  1513. /* =============================================================================
  1514.  *        Drone_GetMark (external)
  1515.  *
  1516.  *    Returns the drone's mark.
  1517.  * ========================================================================== */
  1518. Boolean Drone_GetMark(
  1519.     TDroneObject            inDrone)
  1520. {
  1521.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1522.     
  1523.     return inDrone->mark;
  1524. }
  1525.  
  1526.  
  1527. /* =============================================================================
  1528.  *        Drone_GetPosition (external)
  1529.  *
  1530.  *    Returns the current position in outPosition.
  1531.  * ========================================================================== */
  1532. void Drone_GetPosition(
  1533.     TDroneObject            inDrone,
  1534.     TQ3Point3D*                outPosition)
  1535. {
  1536.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1537.     assert(outPosition != NULL);
  1538.     
  1539.     *outPosition = inDrone->position;
  1540. }
  1541.  
  1542.  
  1543. /* =============================================================================
  1544.  *        Drone_GetDirection (external)
  1545.  *
  1546.  *    Returns the current direction in outDirection.
  1547.  * ========================================================================== */
  1548. void Drone_GetDirection(
  1549.     TDroneObject            inDrone,
  1550.     TQ3Vector3D*            outDirection)
  1551. {
  1552.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1553.     assert(outDirection != NULL);
  1554.     
  1555.     *outDirection = inDrone->direction;
  1556. }
  1557.  
  1558.  
  1559. /* =============================================================================
  1560.  *        Drone_GetUp (external)
  1561.  *
  1562.  *    Returns the current up vector in outUp.
  1563.  * ========================================================================== */
  1564. void Drone_GetUp(
  1565.     TDroneObject            inDrone,
  1566.     TQ3Vector3D*            outUp)
  1567. {
  1568.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1569.     assert(outUp != NULL);
  1570.     
  1571.     *outUp = inDrone->up;
  1572. }
  1573.  
  1574.  
  1575. /* =============================================================================
  1576.  *        Drone_GetMatrix (external)
  1577.  *
  1578.  *    Returns the matrix that transforms to the drone position and orientation.
  1579.  * ========================================================================== */
  1580. void Drone_GetMatrix(
  1581.     TDroneObject            inDrone,
  1582.     TQ3Matrix4x4*            outMatrix)
  1583. {
  1584.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1585.     assert(outMatrix != NULL);
  1586.     
  1587.     outMatrix->value[0][0] = inDrone->direction.x;
  1588.     outMatrix->value[0][1] = inDrone->direction.y;
  1589.     outMatrix->value[0][2] = inDrone->direction.z;
  1590.     
  1591.     outMatrix->value[1][0] = inDrone->up.x;
  1592.     outMatrix->value[1][1] = inDrone->up.y;
  1593.     outMatrix->value[1][2] = inDrone->up.z;
  1594.     
  1595.     outMatrix->value[2][0] = inDrone->cross.x;
  1596.     outMatrix->value[2][1] = inDrone->cross.y;
  1597.     outMatrix->value[2][2] = inDrone->cross.z;
  1598.     
  1599.     outMatrix->value[3][0] = inDrone->position.x;
  1600.     outMatrix->value[3][1] = inDrone->position.y;
  1601.     outMatrix->value[3][2] = inDrone->position.z;
  1602.     
  1603.     outMatrix->value[0][3] = 0.0;
  1604.     outMatrix->value[1][3] = 0.0;
  1605.     outMatrix->value[2][3] = 0.0;
  1606.     outMatrix->value[3][3] = 1.0;
  1607. }
  1608.  
  1609.  
  1610. /* =============================================================================
  1611.  *        Drone_Fire (external)
  1612.  *
  1613.  *    Called each time the fire button is pressed.
  1614.  * ========================================================================== */
  1615. void Drone_Fire(
  1616.     TDroneObject            inDrone)
  1617. {
  1618.     TQ3Vector3D                up;
  1619.     TQ3Vector3D                cross;
  1620.     TQ3Point3D                origin;
  1621.     
  1622.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1623.     
  1624.     // Find the offsets
  1625.     Q3Vector3D_Scale(&inDrone->up,        BULLET_DRONE_OFFSET, &up);
  1626.     Q3Vector3D_Scale(&inDrone->cross,    BULLET_DRONE_OFFSET, &cross);
  1627.     
  1628.     // Fire one
  1629.     Q3Point3D_Vector3D_Subtract(&inDrone->position, &up, &origin);
  1630.     Q3Point3D_Vector3D_Subtract(&origin, &cross, &origin);
  1631.     BulletDrone_New(&origin, &inDrone->direction);
  1632.     
  1633.     // Fire two
  1634.     Q3Point3D_Vector3D_Subtract(&inDrone->position, &up, &origin);
  1635.     Q3Point3D_Vector3D_Add(&origin, &cross, &origin);
  1636.     BulletDrone_New(&origin, &inDrone->direction);
  1637. }
  1638.  
  1639.  
  1640. /* =============================================================================
  1641.  *        Drone_Silence (external)
  1642.  *
  1643.  *    Silences any drone sounds.
  1644.  * ========================================================================== */
  1645. void Drone_Silence(
  1646.     TDroneObject            inDrone)
  1647. {
  1648.     SndCommand                sndCommand;
  1649.     
  1650.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1651.     
  1652.     //• We should really do this as AutoDrone_Silence, but whatever...
  1653.     
  1654.     if (inDrone->autoSndChannel != NULL)
  1655.     {
  1656.         // Purge any pending commands
  1657.         sndCommand.cmd = flushCmd;
  1658.         sndCommand.param1 = 0;
  1659.         sndCommand.param2 = 0;
  1660.         SndDoImmediate(inDrone->autoSndChannel, &sndCommand);
  1661.             
  1662.         // Quiet the current sound
  1663.         sndCommand.cmd = quietCmd;
  1664.         sndCommand.param1 = 0;
  1665.         sndCommand.param2 = 0;
  1666.         SndDoImmediate(inDrone->autoSndChannel, &sndCommand);
  1667.     }
  1668. }
  1669.  
  1670.  
  1671. /* =============================================================================
  1672.  *        SelfDrone_Turn (external)
  1673.  *
  1674.  *    Changes the direction of the ship by the given angles, in radians.
  1675.  * ========================================================================== */
  1676. void SelfDrone_Turn(
  1677.     TDroneObject            inDrone,
  1678.     float                    inHorzAngle,
  1679.     float                    inVertAngle)
  1680. {
  1681.     TQ3Vector3D                v1;
  1682.     TQ3Vector3D                v2;
  1683.     
  1684.     assert(inDrone != NULL && IS_SELF_DRONE(inDrone));
  1685.     
  1686.     if (inHorzAngle != 0.0)
  1687.     {
  1688.         Q3Vector3D_Scale(&inDrone->direction, cosf(inHorzAngle), &v1);
  1689.         Q3Vector3D_Scale(&inDrone->cross,     sinf(inHorzAngle), &v2);
  1690.         Q3Vector3D_Add(&v1, &v2, &inDrone->direction);
  1691.     }
  1692.     
  1693.     if (inVertAngle != 0.0)
  1694.     {
  1695.         Q3Vector3D_Scale(&inDrone->direction, cosf(inVertAngle), &v1);
  1696.         Q3Vector3D_Scale(&inDrone->up,        sinf(inVertAngle), &v2);
  1697.         Q3Vector3D_Add(&v1, &v2, &inDrone->direction);
  1698.     }
  1699. }
  1700.  
  1701.  
  1702.